package p02_容器的经典实现类;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * BeanFactory经典实现类:DefaultListableBeanFactory
 * AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory)加了5个bean到BeanFactory
 * org.springframework.context.annotation.internalConfigurationAnnotationProcessor -> 解析@Configuration,@Bean -> BeanFactory后处理器
 * org.springframework.context.annotation.internalAutowiredAnnotationProcessor -> 解析@Autowired -> Bean后处理器
 * org.springframework.context.annotation.internalCommonAnnotationProcessor -> 解析@Resource(javaEE提供) -> Bean后处理器
 * org.springframework.context.event.internalEventListenerProcessor ->
 * org.springframework.context.event.internalEventListenerFactory ->
 * 这些bean叫后处理器，相当于插件，给BeanFactory增加功能的，但是此时BeanFactory看来这些只是普通bean
 * 等"运行"这些bean，他们才会变成后处理器
 */
public class _1_BeanFactory经典实现 {
    public static void main(String[] args) {
        /**
         * 创建一个原生的beanFactory，相当于创建了ioc容器，bean就是放在这个容器里(实际是放在父类DefaultSingletonBeanRegistry维护的一个map中)
         * 但不能直接放入bean，如果往里面加入bean，需要先往里面添加bean的定义(描述bean长什么样子，class, scope, 生命周期方法...)
         * BeanFactory就可以bean定义对象，创建具体的bean
         */
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        //创建一个bean定义对象，描述某个bean长啥样
        AbstractBeanDefinition configDefinition = BeanDefinitionBuilder
                .genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
        //给beanFactory注册bean定义，beanFactory会根据bean定义创建对应bean对象
        beanFactory.registerBeanDefinition("这是bean名字", configDefinition);

        /**
         * 原生beanFactory功能有限，仅有创建bean的能力，还无法解析@Configuration，@Bean等注解
         * 解析这些注解的工作是交给后处理器的，后处理器扩展了beanFactory的功能，相当于插件
         * 将后处理器加入beanFactory中，运行对应的后处理器。而解析@Configuration，@Bean的工作有BeanFactory后处理器完成
         */
        //AnnotationConfigUtils是spring提供的工具类,作用是向BeanFactory中加入一些后处理器(只是添加了，后处理器还没开始工作)，后处理器本质是bean
        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
        //使得这些后处理器中的BeanFactory类型后处理器生效(后处理器分类：BeanFactory后处理器和Bean后处理器)
        beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(postProcessor -> {
            postProcessor.postProcessBeanFactory(beanFactory);
        });
        //此时该BeanFactory就具有了解析@Configuration，@Bean的能力
        for (String bean : beanFactory.getBeanDefinitionNames()) {
            System.out.println(bean);
        }
        //bean1的bean2属性为null，说明@Autowired没有生效
        //System.out.println("===>bean1的bean2属性是否有值: " + beanFactory.getBean("bean1", Bean1.class).bean2);

        /**
         * BeanFactory后处理器不具有@Autowired解析能力，该注解由Bean后处理器完成，bean后处理器针对bean的生命周期各个阶段进行扩展
         * beanFactory.getBean("bean1", Bean1.class)得到bean1，该bean1的bean2属性为空
         * 欲使BeanFactory能够解析@Autowired，运行加入到容器中的所有Bean后处理器即可
         */
        beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(postProcessor -> {
            //运行bean后处理器的方法与运行BeanFactory后处理器的方法不一样
            beanFactory.addBeanPostProcessor(postProcessor);
        });
        /**
         * 此时bean2就不再为空了
         * 注意：上面不要运行beanFactory.getBean("bean1", Bean1.class).bean2，否则这里同样为空
         *      因为如果上面已经创建了bean1，这里就不会再次创建，这体现了beanFactory中单例bean的创建是懒加载的
         */
        System.out.println("===>bean1的bean2属性是否有值: " + beanFactory.getBean("target", Bean1.class).bean2);

        /**
         * 通过打印的日志发现，BeanFactory并不是一开始就把所有单例bean创建好，而是第一次获取时创建bean，发现依赖了其他bean再继续创建
         * 调用beanFactory.getBean("bean1", Bean1.class)会创建三个bean，分别是bean1，bean1所在配置类bean，bean2
         * 可以手动告诉BeanFactory一次性创建所有单例bean，如果某个bean已经创建了则跳过
         */
        beanFactory.preInstantiateSingletons();

        /**
         * 后处理器的优先级：谁先加入运行，谁优先级高
         * 所以@Autowire和@Resource的优先级，取决于解析他们的后处理器的优先级，默认前者优先级高(不排序的情况下)
         * 可以调用比较器给他们排序，然后运行，优先级就会改变
         * 排序的默认依据取决于他们实现的Ordered的getOrder方法返回值，值越小优先级越高(@Autowired的bean后处理器优先级比较低)
         */

        /**
         * 总结：
         *  原生BeanFactory功能有限
         *      1.不会主动调用BeanFactory后处理器
         *      2.不会主动调用Bean后处理器
         *      3.不会主动初始化单例
         *      4.不会解析#{}, ${}表达式
         */
    }

    @Configuration
    static class Config {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }

        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean1 {
        //bean1又依赖了bean2
        @Autowired
        private Bean2 bean2;
    }

    static class Bean2 {
    }
}
